//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Data;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using JetBrains.Annotations;
using LargoCommon.Abstract;
using LargoCommon.Interfaces;
using LargoCommon.Music;
namespace LargoCommon.Support
{
///
/// MusicXml Measure.
///
public sealed class MusicXmlMeasure {
#region Fields
///
/// Music Xml Header.
///
[UsedImplicitly]
private MusicXmlHeader header;
///
/// Music Xml Reader.
///
private MusicXmlReader reader;
#endregion
#region Constructors
///
/// Initializes a new instance of the MusicXmlMeasure class.
///
/// Musical header.
public MusicXmlMeasure(MusicXmlHeader givenHeader) {
this.Header = givenHeader;
}
///
/// Initializes a new instance of the class.
///
[UsedImplicitly]
public MusicXmlMeasure() {
}
#endregion
#region Properties
///
/// Gets or sets the header.
///
///
/// The header.
///
/// Music Xml Header is null.
/// Music Xml Header cannot be empty.;value
public MusicXmlHeader Header {
get {
Contract.Ensures(Contract.Result() != null);
if (this.header == null) {
throw new InvalidOperationException("Music Xml Header is null.");
}
return this.header;
}
set => this.header = value ?? throw new ArgumentException("Argument cannot be empty.", nameof(value));
}
///
/// Gets or sets the reader.
///
///
/// The reader.
///
private MusicXmlReader Reader {
get {
Contract.Ensures(Contract.Result() != null);
if (this.reader == null) {
throw new InvalidOperationException("Music Xml Reader is null.");
}
return this.reader;
}
set => this.reader = value ?? throw new ArgumentException("Argument cannot be empty.", nameof(value));
}
#endregion
#region Public static methods
///
/// Measure element.
///
/// Bar number.
/// Musical track.
/// The header.
///
/// Returns value.
///
public static XElement MeasureElement(int barNumber, MusicalLine track, MusicalHeader header) {
Contract.Requires(track != null);
var measure = new XElement(
"measure",
new XAttribute("number", barNumber.ToString(CultureInfo.InvariantCulture)));
var beatDivision = header.Division / header.Metric.MetricBeat;
var attributes = new XElement(
"attributes",
new XElement("divisions", beatDivision.ToString(CultureInfo.InvariantCulture)));
if (barNumber == 1) {
var element = KeyElement(); //// track
attributes.Add(element);
element = TimeElement(header);
attributes.Add(element);
element = ClefElement(track);
if (!element.IsEmpty) {
attributes.Add(element);
}
//// element = this.TempoElement();
//// attributes.Add(element);
}
var tones = track.MusicalTonesInBar(barNumber);
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var tone in tones) {
var mtone = tone as MusicalTone;
var noteElement = mtone?.Pitch != null ? NoteElement(mtone, beatDivision)
: RestElement(tone, beatDivision);
measure.Add(noteElement);
}
measure.Add(attributes);
return measure;
}
#endregion
#region Public methods
///
/// Read Musical Bar.
///
/// The score part object.
/// Musical Part.
/// Musical measure.
/// Bar number.
/// The given reader.
public void ReadMusicalBar(ScorePartObject scorePartObject, MusicalPart musicalPart, XContainer measure, int barNumber, MusicXmlReader givenReader) {
Contract.Requires(measure != null);
Contract.Requires(musicalPart != null);
this.Reader = givenReader;
var attributes = measure.Element("attributes");
//// XElement key = attributes.Element("key");
//// XElement clef = attributes.Element("clef");
var transpose = attributes?.Element("transpose");
if (transpose != null) {
this.Reader.LocalPitchShift = 0;
//// XElement diatonic = transpose.Element("diatonic");
//// XElement octaveDouble = transpose.Element("double");
var octaveChange = transpose.Element("octave-change");
if (octaveChange != null) {
this.Reader.LocalPitchShift += (int)octaveChange * DefaultValue.HarmonicOrder;
}
var chromatic = transpose.Element("chromatic");
if (chromatic != null) {
this.Reader.LocalPitchShift += (int)chromatic;
}
}
var actions = measure.Nodes(); //// Elements("note");
foreach (var action in
actions.Where(node => node.NodeType == XmlNodeType.Element).OfType()) {
this.AppendMusicalTones(scorePartObject, musicalPart, barNumber, action);
}
}
#endregion
#region Private static methods
///
/// Note element.
///
/// Melodic tone.
/// Beat division.
/// Returns value.
private static XElement NoteElement(MusicalTone mtone, int beatDivision) {
Contract.Requires(mtone != null);
Contract.Requires(mtone.Pitch != null);
var quotient = 1.0f * mtone.Duration / beatDivision;
var noteType = MusicXmlHeader.GetNoteType(quotient);
var pitch = new XElement(
"pitch",
new XElement("step", MusicalProperties.GetSingleNoteName(mtone.Pitch.Element)),
new XElement("alter", MusicalProperties.GetAlterSign(mtone.Pitch.Element).ToString(CultureInfo.InvariantCulture)),
new XElement("octave", mtone.Pitch.StandardOctave.ToString(CultureInfo.InvariantCulture)));
var note = new XElement(
"note",
pitch,
new XElement("duration", mtone.Duration.ToString(CultureInfo.InvariantCulture)),
new XElement("type", noteType));
return note;
}
///
/// Note element.
///
/// Musical Tone.
/// Beat division.
/// Returns value.
private static XElement RestElement(IMusicalTone mtone, int beatDivision) {
Contract.Requires(mtone != null);
var quotient = 1.0f * mtone.Duration / beatDivision;
var noteType = MusicXmlHeader.GetNoteType(quotient);
var note = new XElement(
"note",
new XElement("rest", null),
new XElement("duration", mtone.Duration.ToString(CultureInfo.InvariantCulture)),
new XElement("type", noteType));
return note;
}
///
/// Read Musical shift.
///
/// Musical shift.
/// Bar number.
/// Sign of the shift.
/// Time quotient.
/// Returns value.
private static MusicalShift ReadMusicalShift(XContainer shift, int barNumber, short signum, double quotient) {
Contract.Requires(shift != null);
var durationElement = shift.Element("duration");
if (durationElement == null) {
return null;
}
var musicalShift = new MusicalShift { BarNumber = barNumber };
var duration = (int)((int)durationElement * quotient);
musicalShift.Value = (short)(signum * duration);
var staff = shift.Element("staff");
if (staff != null) {
musicalShift.Staff = (byte)(int)staff;
}
var voice = shift.Element("voice");
if (voice != null) {
musicalShift.Voice = (byte)(int)voice;
}
return musicalShift;
}
///
/// Key element.
///
/// Returns value.
private static XElement KeyElement() { //// MusicalLine track
const int fifthNumber = 0;
var key = new XElement(
"key",
new XElement("fifths", fifthNumber.ToString(CultureInfo.InvariantCulture)),
new XElement("mode", "major"));
return key;
}
///
/// Time element.
///
/// The header.
///
/// Returns value.
///
private static XElement TimeElement(MusicalHeader header) {
var key = new XElement(
"time",
new XElement("beats", header.Metric.MetricBeat.ToString(CultureInfo.InvariantCulture)),
new XElement("beat-type", header.Metric.MetricGround.ToString(CultureInfo.InvariantCulture)));
return key;
}
///
/// Clef element.
///
/// Musical track.
/// Returns value.
private static XElement ClefElement(MusicalLine track) {
Contract.Requires(track != null);
var clef = new XElement("clef");
var bandType = MusicalProperties.BandTypeFromOctave(track.FirstStatus.Octave); //// (MusicalBand)track.BandType
switch (bandType) {
case MusicalBand.HighBeat: {
clef.Add(new XElement("sign", "G"));
clef.Add(new XElement("line", "2"));
}
break;
case MusicalBand.MiddleBeat: {
clef.Add(new XElement("sign", "G"));
clef.Add(new XElement("line", "2"));
}
break;
case MusicalBand.BassTones: {
clef.Add(new XElement("sign", "F"));
clef.Add(new XElement("line", "4"));
}
break;
case MusicalBand.Any:
break;
case MusicalBand.MiddleTones:
break;
case MusicalBand.HighTones:
break;
case MusicalBand.BassBeat:
break;
//// resharper default: break;
}
return clef;
}
#endregion
#region Private methods
///
/// Appends the musical tones.
///
/// The score part object.
/// The musical part.
/// The bar number.
/// The action.
private void AppendMusicalTones(ScorePartObject scorePartObject, MusicalPart musicalPart, int barNumber, XElement action) {
Contract.Requires(musicalPart != null);
Contract.Requires(action != null);
double quotient = 1.0f * this.Reader.CommonDivision / this.Reader.LocalDivision; //// this.Reader.MusicalBlock.RhythmicOrder; //// ;
quotient = quotient * this.Reader.Block.Header.System.RhythmicOrder / this.Reader.CommonDivision;
if (action.Name == "backward") {
var shift = ReadMusicalShift(action, barNumber, -1, quotient);
if (shift != null) {
musicalPart.AddMusicalObject(shift);
}
}
if (action.Name == "forward") {
var shift = ReadMusicalShift(action, barNumber, +1, quotient);
if (shift != null) {
musicalPart.AddMusicalObject(shift);
}
}
// ReSharper disable once InvertIf
if (action.Name == "note") {
var tone = this.ReadMusicalTone(action, barNumber, quotient);
var chord = action.Element("chord");
if (chord != null) {
if (tone != null) {
var shift = new MusicalShift {
BarNumber = barNumber,
Value = (short)-tone.Duration,
Voice = tone.Voice,
Staff = tone.Staff
};
musicalPart.AddMusicalObject(shift);
}
}
if (tone == null) {
return;
}
tone.InstrumentNumber = scorePartObject.MidiProgram;
//// tone.Channel = (MidiChannel)scorePartObject.MidiChannel;
musicalPart.AddMusicalObject(tone);
}
}
///
/// Read Musical Tone.
///
/// Musical note.
/// Bar number.
/// Time quotient.
/// Returns value.
private MusicalStrike ReadMusicalTone(XContainer note, int barNumber, double quotient) {
Contract.Requires(note != null);
MusicalStrike tone;
var pitch = note.Element("pitch");
//// XElement rest = note.Element("rest");
var durationElement = note.Element("duration");
if (durationElement == null) {
return null;
}
var duration = (int)((int)durationElement * quotient); //// in ticks
//// BitRange range = new BitRange(this.Reader.MusicalBlock.RhythmicOrder, bitFrom, duration);
if (pitch != null) {
var step = (string)pitch.Element("step");
var alterString = (string)pitch.Element("alter");
var alter = (short)(alterString != null ? short.Parse(alterString, CultureInfo.InvariantCulture) : 0);
var octave = (string)pitch.Element("octave");
//// byte.Parse(step)
var noteNumber = MusicalProperties.GetNoteNumber(step, alter);
var element = noteNumber >= 0 ? (byte)(noteNumber % this.Reader.Block.Header.System.HarmonicOrder)
: (byte)(noteNumber + this.Reader.Block.Header.System.HarmonicOrder);
if (element >= DefaultValue.HarmonicOrder) {
throw new DataException("Invalid Musical Element");
}
var hs = HarmonicSystem.GetHarmonicSystem(this.Reader.Block.Header.System.HarmonicOrder);
var musicalPitch = new MusicalPitch(hs, (short)(short.Parse(octave, CultureInfo.InvariantCulture) + 1), element);
if (this.Reader.LocalPitchShift != 0) {
musicalPitch.SetAltitude(musicalPitch.SystemAltitude + this.Reader.LocalPitchShift);
}
tone = new MusicalTone(musicalPitch, this.Reader.Block.Header.System.RhythmicOrder, (byte)duration, MusicalLoudness.MeanLoudness, barNumber);
}
else {
tone = new MusicalStrike(MusicalToneType.Empty, this.Reader.Block.Header.System.RhythmicOrder, (byte)duration, MusicalLoudness.None, barNumber);
}
var staff = note.Element("staff");
if (staff != null) {
tone.Staff = (byte)(int)staff;
}
var voice = note.Element("voice");
if (voice != null) {
tone.Voice = (byte)(int)voice;
}
return tone;
}
#endregion
}
}